package com.applink.sell_proof.urlimageviewhelper;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.FloatMath;
import android.view.MotionEvent;
import android.view.animation.AnimationUtils;
import android.widget.ImageView;

import com.applink.sell_proof.constant.Constant;

public class RotateImageView extends ImageView {
	private static final int NONE = 0;

	@SuppressWarnings("unused")
	private static final String TAG = "RotateImageView";
	private static final int ANIMATION_SPEED = 180 * 2;
	private Context mContext;
	private int mCurrentDegree = 0;
	private int mStartDegree = 0;
	private int mTargetDegree = 0;

	private float scaleFactor = 1.0f;
	private boolean mClockwise = false;
	private long mAnimationStartTime = 0L;
	private long mAnimationEndTime = 0L;
	// private ArrayList touchPoints = null;
	// private boolean isMultiTouch = false;
	// private int pathEffectPhase = 0;
	// private PointF centerPoint;
	private PointF lastPoint;
	private PointF lastPointPress;
	private boolean editEnable = true;
	private boolean swipeEnable = false;
	private float oldDist = 0.0f;
	private float newDist = 0.0f;
	private int scaleW = -1;
	private int scaleH = -1;
	private int lastTouchDegree = -1;
	private boolean defaultDrawing = false;

	private int mode = NONE;
	private float minScale = 1f;
	private float saveScale = 1f;

	private int deltaX = 0;
	private int deltaY = 0;

	private int lastDeltaX = 0;
	private int lastDeltaY = 0;

	private int beginAngle = 0;

	private boolean isPressed = false, isMoved = false;
	private PointF lastPointPressSwipe = new PointF();

	private boolean isRequestEffect = false;

	private boolean useRoundCorner = true;
	private boolean useRotateMode = true;
	private boolean useZoomMode = true;
	private boolean useFitBoundMode = false;
	int w, h;

	Path clipPath = null;
	public boolean onLeftSide = false, onTopSide = false, onRightSide = false,
			onBottomSide = false;

	private float maxScaleZoom = 5f;
	private float right, bottom, origWidth, origHeight, bmWidth, bmHeight;
	private float width, height;
	private Matrix matrix = new Matrix();

	//
	// private CreateNewKard parentActivity = null;
	// private CreateNewKard parentActivitySwipe = null;

	public RotateImageView(Context context) {
		super(context);
		super.setClickable(true);
		initialize(context);
		this.mContext = context;
	}

	public RotateImageView(Context context, int defStyle) {
		super(context, null, defStyle);
		initialize(context);
		super.setClickable(true);
		this.mContext = context;
	}

	public RotateImageView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		initialize(context);
		super.setClickable(true);
		this.mContext = context;
	}

	public RotateImageView(Context context, AttributeSet attrs) {
		super(context, attrs);
		initialize(context);
		super.setClickable(true);
		this.mContext = context;
	}

	public void setUseRoundCorner(boolean values) {
		useRoundCorner = values;
	}

	public void setUseRotateMode(boolean values) {
		useRotateMode = values;
	}

	public void setUseZoomMode(boolean values) {
		useZoomMode = values;
	}

	public void setUseFitBoundMode(boolean values) {
		useFitBoundMode = values;
	}

	public void doRotate(int degree) {
		setDegree(degree);
	}

	protected void setDegree(int degree) {
		// make sure in the range of [0, 359]
		degree = degree >= 0 ? degree % 360 : degree % 360 + 360;
		if (degree == mTargetDegree)
			return;

		mTargetDegree = degree;
		mStartDegree = mCurrentDegree;
		mAnimationStartTime = AnimationUtils.currentAnimationTimeMillis();

		int diff = mTargetDegree - mCurrentDegree;
		diff = diff >= 0 ? diff : 360 + diff; // make it in range [0, 359]

		// Make it in range [-179, 180]. That's the shorted distance between the
		// two angles
		diff = diff > 180 ? diff - 360 : diff;

		mClockwise = diff >= 0;
		mAnimationEndTime = mAnimationStartTime + Math.abs(diff) * 1000
				/ ANIMATION_SPEED;

		invalidate();
		System.out.println(">>>> degree = " + degree);
		Constant.DEGREES_ROTATION = degree;
	}

	@Override
	protected void onDraw(Canvas canvas) {
		Drawable drawable = getDrawable();
		if (drawable == null)
			return;
		if (isRequestEffect && drawable != null) {
			isRequestEffect = false;
		}

		Rect bounds2 = drawable.getBounds();
		if (scaleW == -1) {
			scaleW = getWidth();
			scaleH = Math.round(getWidth() * (bounds2.bottom + bounds2.top)
					/ (bounds2.right + bounds2.left));
			if (getHeight() > scaleH) {
				scaleH = getHeight();
				scaleW = Math.round(getHeight()
						* (bounds2.right + bounds2.left)
						/ (bounds2.bottom + bounds2.top));
			}
		}
		// end
		Rect bounds = drawable.getBounds();

		w = Math.round(scaleFactor * (bounds.left + scaleW));
		h = Math.round(scaleFactor * (bounds.top + scaleH));
		drawable.setBounds(bounds.left, bounds.top, w, h);
		if (w == 0 || h == 0)
			return;
		if (mCurrentDegree != mTargetDegree) {
			long time = AnimationUtils.currentAnimationTimeMillis();
			if (time < mAnimationEndTime) {
				int deltaTime = (int) (time - mAnimationStartTime);
				int degree = mStartDegree
						+ (ANIMATION_SPEED * (mClockwise ? deltaTime
								: -deltaTime)) / 1000;
				degree = degree < 0 ? degree % 360 + 360 : degree % 360;
				mCurrentDegree = degree;
				invalidate();
			} else {
				mCurrentDegree = mTargetDegree;
			}
		}
		int left = getPaddingLeft();
		int top = getPaddingTop();
		int right = getPaddingRight();
		int bottom = getPaddingBottom();
		int width = getWidth() - left - right;
		int height = getHeight() - top - bottom;
		int saveCount = canvas.getSaveCount();
		canvas.translate(left + width / 2 + deltaX, top + height / 2 + deltaY);
		canvas.rotate(-mCurrentDegree);
		canvas.translate(-w >> 1, -h >> 1);
		drawable.draw(canvas);
		canvas.restoreToCount(saveCount);
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		super.onTouchEvent(event);
		if (swipeEnable) {
			int action = event.getAction() & MotionEvent.ACTION_MASK;
			switch (action) {
			case MotionEvent.ACTION_DOWN:
				isPressed = true;
				lastPointPressSwipe.set(event.getX(0), event.getY(0));
				break;

			case MotionEvent.ACTION_POINTER_DOWN:
				break;

			case MotionEvent.ACTION_POINTER_UP:
				break;

			case MotionEvent.ACTION_UP:
				if (lastPointPressSwipe.x != -1 && isMoved
						&& event.getX(0) > lastPointPressSwipe.x - 30) {
					// parentActivitySwipe.swipeLeftTemplate();
				}
				if (lastPointPressSwipe.x != -1 && isMoved
						&& event.getX(0) < lastPointPressSwipe.x - 30) {
					// parentActivitySwipe.swipeRightTemplate();
				}
				isPressed = false;
				isMoved = false;
				lastPointPressSwipe.set(-1F, -1F);
				break;

			case MotionEvent.ACTION_MOVE:
				isMoved = true;
				break;
			}
			return true;
		}
		if (!editEnable)
			// TCuong 07 - 12 - 2012 return touch event for parent view - fix
			// bug KB not hide
			return false;
		// end
		int action = event.getAction() & MotionEvent.ACTION_MASK;
		try {
			switch (action) {
			case MotionEvent.ACTION_DOWN:
				lastPointPress.set(event.getX(0), event.getY(0));
				invalidate();
				break;

			case MotionEvent.ACTION_POINTER_DOWN:
				// lastPointPress.set(event.getX(1), event.getY(1));
				// isMultiTouch = true;
				beginAngle = rotation(event);
				oldDist = spacing(event);
				invalidate();
				break;

			case MotionEvent.ACTION_POINTER_UP:
				// isMultiTouch = false;
				lastPoint.set(-1F, -1F);
				// lastDeltaX = deltaX;
				// lastDeltaY = deltaY;
				lastPointPress.set(-1F, -1F);
				if (scaleW > -1) {
					if (scaleW > scaleH) {
						scaleW = Math.round(scaleFactor * scaleH) > 10 ? Math
								.round(scaleFactor * scaleW) : 10 * getWidth()
								/ getHeight();
						scaleH = Math.round(scaleFactor * scaleH) > 10 ? Math
								.round(scaleFactor * scaleH) : 10;
					} else {
						scaleW = Math.round(scaleFactor * scaleW) > 10 ? Math
								.round(scaleFactor * scaleW) : 10;
						scaleH = Math.round(scaleFactor * scaleW) > 10 ? Math
								.round(scaleFactor * scaleH) : 10 * getHeight()
								/ getWidth();
					}
				}
				scaleFactor = 1.0F;
				lastTouchDegree = mTargetDegree;
				// lastDeltaX = deltaX;
				// lastDeltaY = deltaY;
				// lastTouchDegree += mCurrentDegree;
				break;

			case MotionEvent.ACTION_UP:
				// isMultiTouch = false;
				lastPoint.set(-1F, -1F);
				lastPointPress.set(-1F, -1F);
				if (scaleW > -1) {
					if (scaleW > scaleH) {
						scaleW = Math.round(scaleFactor * scaleH) > 10 ? Math
								.round(scaleFactor * scaleW) : 10 * getWidth()
								/ getHeight();
						scaleH = Math.round(scaleFactor * scaleH) > 10 ? Math
								.round(scaleFactor * scaleH) : 10;
					} else {
						scaleW = Math.round(scaleFactor * scaleW) > 10 ? Math
								.round(scaleFactor * scaleW) : 10;
						scaleH = Math.round(scaleFactor * scaleW) > 10 ? Math
								.round(scaleFactor * scaleH) : 10 * getHeight()
								/ getWidth();
					}
				}
				scaleFactor = 1.0F;
				lastTouchDegree = mTargetDegree;
				lastDeltaX = deltaX;
				lastDeltaY = deltaY;
				// lastTouchDegree += mCurrentDegree;
				break;

			case MotionEvent.ACTION_MOVE:
				if (event.getPointerCount() > 1) {
					if (lastPoint.x == -1F) {
						lastPoint.set(Math.abs(event.getX(0) - event.getX(1)),
								Math.abs(event.getY(0) - event.getY(1)));
					} else {
						int width = getWidth() - getPaddingLeft()
								- getPaddingRight();
						int height = getHeight() - getPaddingTop()
								- getPaddingBottom();
						if ((width / 2 + Math.abs(deltaX) <= Math
								.round(spacing(event)
										/ oldDist
										* (getDrawable().getBounds().left + scaleW)) >> 1 && height
								/ 2 + Math.abs(deltaY) <= Math
								.round(spacing(event)
										/ oldDist
										* (getDrawable().getBounds().top + scaleH)) >> 1)
								|| !useFitBoundMode) {
							if (useZoomMode) {
								scaleFactor = spacing(event) / oldDist;
							}
						}
						// Log.d("debug degree lastTouchDegree", "  " +
						// lastTouchDegree);
						// Log.d("debug degree angle", "  " + (beginAngle -
						// rotation(event)));
						// Log.d("debug degree", "  " + (beginAngle -
						// rotation(event) + lastTouchDegree));
						// if(Math.abs(beginAngle - rotation(event)) < 180);
						if (useRotateMode) {
							setDegree(beginAngle - rotation(event)
									+ lastTouchDegree);// > 10 ? rotation(event)
														// + mCurrentDegree :
														// mCurrentDegree);
						}
					}
				} else {
					if (lastPointPress.x == -1F) {
						lastPointPress.set(event.getX(0), event.getY(0));
					} else {
						int width = getWidth() - getPaddingLeft()
								- getPaddingRight();
						int height = getHeight() - getPaddingTop()
								- getPaddingBottom();
						if (width / 2 + Math.abs(getDeltaX(event) + lastDeltaX) <= w >> 1
								|| !useFitBoundMode) {
							deltaX = getDeltaX(event) + lastDeltaX;
						}
						if (height / 2
								+ Math.abs(getDeltaY(event) + lastDeltaY) <= h >> 1
								|| !useFitBoundMode) {
							deltaY = getDeltaY(event) + lastDeltaY;
						}
					}
				}
				invalidate();
				break;
			}
		} catch (Exception exception) {
		}
		return true;
	}

	private void initialize(Context context) {
		// touchPoints = new ArrayList();
		lastPoint = new PointF();
		lastPoint.set(-1F, -1F);
		lastPointPress = new PointF();
		lastPointPress.set(-1F, -1F);
		editEnable = true;
	}

	// private void setPoints(MotionEvent event) {
	// touchPoints.clear();
	// int pointerIndex = 0;
	// for (int index = 0; index < event.getPointerCount(); index++) {
	// pointerIndex = event.getPointerId(index);
	// touchPoints.add(new PointF(event.getX(pointerIndex), event
	// .getY(pointerIndex)));
	// }
	//
	// }

	private int rotation(MotionEvent event) {
		double delta_x = event.getX(0) - event.getX(1);
		double delta_y = event.getY(0) - event.getY(1);

		if (delta_x == 0) {
			return 0;
		} else {
			double radians;
			// if (delta_x > 0){
			radians = Math.atan2(delta_y, delta_x);
			return (int) Math.round(Math.toDegrees(radians));
			// }
			// else{
			// radians = Math.atan2(delta_y, -delta_x);
			// return 180 + (int) Math.round(Math.toDegrees(radians));
			// }
		}
	}

	private float spacing(MotionEvent event) {
		float x = event.getX(0) - event.getX(1);
		float y = event.getY(0) - event.getY(1);
		return FloatMath.sqrt(x * x + y * y);
	}

	private int getDeltaX(MotionEvent event) {
		return Math.round(event.getX(0) - lastPointPress.x);
	}

	private int getDeltaY(MotionEvent event) {
		return Math.round(event.getY(0) - lastPointPress.y);
	}

	private PointF getMidPoint(float x1, float y1, float x2, float y2) {
		PointF point = new PointF();
		float x = x1 + x2;
		float y = y1 + y2;
		point.set(x / 2.0F, y / 2.0F);
		return point;
	}

	public void setEdit(boolean enable) {
		editEnable = enable;
	}

	// public void setSwipeEnable(boolean enable, CreateNewKard instance) {
	// if (enable) {
	// swipeEnable = enable;
	// parentActivitySwipe = instance;
	// } else {
	// swipeEnable = enable;
	// parentActivitySwipe = null;
	// }
	// }

	public boolean getSwipeEnable() {
		return swipeEnable;
	}

	public void setDefaultDrawing(boolean enable) {
		defaultDrawing = enable;
	}

	public void reset() {
		// TCuong 10 - 12 - 2012 fix scale image input from camera or local file
		scaleW = -1;// getWidth();
		scaleH = -1;// getHeight();
		// end
		setDegree(0);
		scaleFactor = 1.0F;
		lastTouchDegree = 0;
		lastDeltaX = 0;
		lastDeltaY = 0;
		deltaX = 0;
		deltaY = 0;
	}

	public int getImageX() {
		return lastDeltaX != -1 ? lastDeltaX : 0;
	}

	public int getImageY() {
		return lastDeltaY != -1 ? lastDeltaY : 0;
	}

	public int getImageWidth() {
		return scaleW;
	}

	public int getImageHeight() {
		return scaleH;
	}

	public void setImageX(int x) {
		deltaX = x;
		lastDeltaX = x;
	}

	public void setImageY(int y) {
		deltaY = y;
		lastDeltaY = y;
	}

	public void setImageWidth(int w) {
		scaleW = w;
	}

	public void setImageHeight(int h) {
		scaleH = h;
	}

	public boolean pagerCanScroll() {
		if (mode != NONE)
			return false;
		return saveScale == minScale;
	}

	// public void requestEffect(CreateNewKard instance) {
	// isRequestEffect = true;
	// parentActivity = instance;
	// }

	public void onZoom(boolean zoomIn) {

		float mScaleFactor = zoomIn ? 1.2f : 0.75f;

		float origScale = saveScale;
		saveScale *= mScaleFactor;

		if (saveScale > maxScaleZoom) {
			saveScale = maxScaleZoom;
			mScaleFactor = maxScaleZoom / origScale;
		} else if (saveScale < minScale) {
			saveScale = minScale;
			mScaleFactor = minScale / origScale;
		}

		if (origWidth * saveScale <= width || origHeight * saveScale <= height)
			matrix.postScale(mScaleFactor, mScaleFactor, width / 2, height / 2);
		else
			matrix.postScale(mScaleFactor, mScaleFactor, width / 2, height / 2);

		// matrix.getValues(m);
		// calcPadding();
		// checkAndSetTranslate(0, 0);

		// Log.w(TAG, "right : " + right);
		// Log.w(TAG, "width : " + width);
		// Log.w(TAG, "bottom : " + bottom);
		// Log.w(TAG, "height : " + height);
		// Log.w(TAG, "redundantXSpace : " + redundantXSpace);
		// Log.w(TAG, "redundantYSpace : " + redundantYSpace);

		setImageMatrix(matrix);
		invalidate();
	}

}